<template>
    <div class="bg"></div>
    <div id="app">
        <div class="bookmark" id="bookmark">
            <!-- 顶部操作区域 -->
            <div class="tool-bar">
                <div class="tool-logo">
                    <a href="" target="_blank">
                        <img src="./assets/svg/logo.svg" title="感谢作者 是半夏鸭 设计的图标" class="tool-icon"/>
                        红隼书签
                    </a>
                </div>
                <div>
                    <div class="search-box">
                        <img src="./assets/svg/search.svg">
                        <input type="text" placeholder="请输入书签名称" v-model.lazy="searchVal"/>
                    </div>
                    <img src="./assets/svg/add.svg" class="tool-icon" @click="add()"/>
                    <a title="我的博客" href="https://www.baidu.com" target="_blank">
                        <img src="./assets/svg/blog.svg" class="tool-icon"/>
                    </a>
                    <a title="在线翻译" href="https://translate.google.cn" target="_blank">
                        <img src="./assets/svg/translate.svg" class="tool-icon"/>
                    </a>
                    <span class="login-s" @click="loginClick">
                        <img src="./assets/svg/user.svg" class="tool-icon" title="已登录"/>
                        <span class="login-status" :title="userInfo.username">{{ userInfo.username.slice(0, 5) }}</span>
                    </span>
                </div>
                <!-- userInfo.objectId?LoginOut:handleUserLogin -->
            </div>
            <!-- 书签内容区域 -->
            <div class="box-m">
                <!-- 侧边导航栏 -->
                <div class="left-box">
                    <div class="left-box-item">
                        <div class="label" :class="activeIndex===index?'active':'inactive'" v-for="(item,index) in folder" :key="item.id" @click="selectType(item,index)">
                            <img src="./assets/svg/file.svg"/>
                            <div class="text-elipss"> {{ item.desc }}</div>
                        </div>
                        <div class="label" @click="addTop()">
                            <div class="logo-box" style="text-align: center">
                                <svg t="1666855950714" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4100" width="50" height="50" xmlns:xlink="http://www.w3.org/1999/xlink">
                                    <path
                                        d="M925.696 384q19.456 0 37.376 7.68t30.72 20.48 20.48 30.72 7.68 37.376q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68l-287.744 0 0 287.744q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888l0-287.744-287.744 0q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68l287.744 0 0-287.744q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68q39.936 0 68.096 28.16t28.16 68.096l0 287.744 287.744 0z"
                                        p-id="4101" fill="#eaeaea"></path>
                                </svg>
                            </div>
                        </div>
                    </div>
                    <!-- 导入导出 -->
                    <div class="import-tool">
                        <span class="import-text">导入/<span style="">导出</span>：&nbsp;</span>
                        <i class="el-icon-upload2" title="导入浏览器书签" @click="importBookmark">
                            <input type="file" ref="filElem" id="file">
                        </i>
                        <i class="el-icon-download" title="导出浏览器书签" @click="exportBookmark"></i>
                    </div>
                </div>
                <!-- 书签展示区域 -->
                <div class="right-box">
                    <!-- 返回上级按钮 -->
                    <div class="go-back-box" style="width: 30px; height:30px;padding-left:20px">
                        <img v-if="idChain.length === 1" style="width: 20px; height:20px" src='./assets/svg/back.svg' alt="">
                        <!--<img v-if="idChain.length > 1" style="width: 20px; height:20px" src='./assets/svg/back2.svg' alt="" @click="goBack">-->
                        <i v-if="idChain.length > 1" class="el-icon-back" @click="goBack"></i>
                    </div>
                    <!-- 书签内容显示 -->
                    <div v-if="searchVal.length === 0" name="bookmark" class="card-s" tag="ul" :css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave">
                        <div class="card-item list-complete-item" v-for="card in bookMark" :key="card.id" @click="navigate(card)">
                            <div class="logo-img">
                                <img :src="card.logo?card.logo:'/img/logo.f38dc2e8.svg'"/>
                            </div>
                            <div class="logo-box">
                                <span class="logo-box-tools">
                                  <i class="el-icon-edit" @click.stop="edit(card)"></i>
                                  <i class="el-icon-delete" @click.stop="del(card)"></i>
                                </span>
                                <span class="title">{{ card.title || "default-title" }}</span>
                                <span class="subtitle">{{ card.desc || "default-desc" }}</span>
                            </div>
                        </div>
                        <div class="card-item list-complete-item" @click="add()">
                            <div class="logo-box" style="text-align: center">
                                <svg t="1666855950714" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4100" width="50" height="50" xmlns:xlink="http://www.w3.org/1999/xlink">
                                    <path
                                        d="M925.696 384q19.456 0 37.376 7.68t30.72 20.48 20.48 30.72 7.68 37.376q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68l-287.744 0 0 287.744q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888l0-287.744-287.744 0q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68l287.744 0 0-287.744q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68q39.936 0 68.096 28.16t28.16 68.096l0 287.744 287.744 0z"
                                        p-id="4101" fill="#eaeaea"></path>
                                </svg>
                            </div>
                        </div>
                    </div>
                    <!-- 搜索结果显示 -->
                    <div v-if="searchVal.length > 0" name="searchmark" class="card-s" tag="ul" :css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave">
                        <div class="card-item list-complete-item" v-for="card in searchMark" :key="card.id" @click="searchNavigate(card)">
                            <div class="logo-img">
                                <img :src="card.logo?card.logo:'/img/logo.f38dc2e8.svg'"/>
                            </div>
                            <div class="logo-box">
                                <!-- <span>{{ xxx }}</span> 中的内容，将直接被解析为string，如果内容包含html富文本，vue使用v-html = "xxx"，且<span></span>不需要填写{{ xxx }} -->
                                <span class="title" v-html="card.title"></span>
                                <span class="subtitle" v-html="card.desc"></span>
                            </div>
                        </div>
                    </div>
                    <!-- 无数据显示 -->
                    <div v-if="Object.keys(bookMark).length === 0 && Object.keys(searchMark).length === 0" class="card-item-nodata" name="nodata">
                        <div>
                            <svg width="66" height="68" viewBox="0 0 66 68" class="icon empty-icon" data-v-8739e5ce="">
                                <g fill="none" fill-rule="evenodd" transform="translate(4 3)" data-v-8739e5ce="">
                                    <g fill="#F7F7F7" data-v-8739e5ce="">
                                        <path d="M9 10h23.751v3.221H9zM9 16.494h41.083v4.026H9zM9 26.104h23.751v3.221H9zM9 42.208h23.751v3.221H9zM9 33.351h41.083v4.026H9zM9 49.455h41.083v4.026H9z" data-v-8739e5ce="">
                                        </path>
                                    </g>
                                    <rect width="56" height="60" x="1.139" y="1.338" stroke="#EBEBEB" stroke-width="2" rx="6" data-v-8739e5ce=""></rect>
                                </g>
                            </svg>
                            <span class="empty-text" data-v-8739e5ce="">暂无数据</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- 新增/修改弹窗 -->
    <Dialog class="my-dialog" v-model="isDetailVisible" @closeViews="closeViews" :detail="toDialog" @refresh="refresh"/>
    <!-- 登录弹窗 -->
    <Login v-model="isLoginVisible" @closeViews="closeLoginViews" @setUser="setUsername" @toRegister="loginToRegister" />
    <!-- 注册弹窗 -->
    <Register v-model="isRegisterVisible" @closeViews="closeRegisterViews" @toLogin="registerToLogin" />
</template>
<script>
import {reactive, toRefs} from '@vue/reactivity'
// import {myData} from './assets/Json/印象笔记.js'
import {testData} from './assets/Json/testData'
import {watch} from '@vue/runtime-core'
import Dialog from './components/Dialog-json.vue'
import Login from './components/Login-json.vue'
import Register from "./components/Register"
import gsap from 'gsap'
import {ElMessage, ElMessageBox} from 'element-plus'
import {saveObject} from './Api/common.js'
import {exportBookmark, walkBookmarksTree} from './components/utils.js'
import Cookie from 'js-cookie'
import Api from './Api/user.js'
// import BookmarkApi from './Api/common2.js'
import BookmarkApi from './Api/bookmark.js'
import Tools from './utils/tools'
import CacheStore from './utils/cacheStore'

// 主要用到map状数据，list使用较少，这里注释掉，只在用到时用工具类生成
// 书签源数据，树状
// var listTree = []
// var mapTree = {}
// 书签源数据
// var listData = []
// var mapData = {}
export default {
    components: {Dialog, Login, Register},
    name: 'kestrel-bookmark',
    setup() {
        const data = reactive({
            // 书签源数据，树状
            mapTree: {},
            // 书签源数据，平级
            mapData: {},
            // 左侧目录激活标志索引
            activeIndex: '',
            // 左侧目录列表
            folder: {},
            // 右侧当前目录下的内容，每次切换目录更新
            bookMark: {},
            // 右侧当前目录下的内容，bookMark -> bookMarkView -> view，做一层隔离，bookMarkView里可对数据添加一些格式
            bookMarkView: [],
            // 搜索匹配书签内容，根据搜索内容更新
            searchMark: [],
            // 全局搜索值
            searchVal: '',
            // 目录id链，不包含当前目录id。这个主要在返回上级时有用
            backChain: [],
            // 目录id链，包含当前目录id。idChain现已取代backChain
            idChain: [],
            // 书签编辑弹窗控制
            isDetailVisible: false,
            // 登录弹窗控制
            isLoginVisible: false,
            // 注册弹窗控制
            isRegisterVisible: false,
            // 书签编辑弹窗参数传递
            toDialog: {},
            userInfo: {
                userid: '',
                username: '未登录'
            }
        })

        const getBookmarkList = () => {
            BookmarkApi.findMineBookmark().then(res => {
                console.log('get bookmark from server')
                console.log(res)
                console.log(res.body)
                // listData = Tools.deepCopy(res.body.list)
                // listTree = Tools.deepCopy(res.body.listTree)
                // data.mapData = Tools.deepCopy(res.body.map)
                data.mapTree = Tools.deepCopy(res.body.data)
                data.mapData = Tools.flatTree(data.mapTree)
                // let listTree = Tools.mapTreeToListTree(mapTree)
                // console.log('getBookmark->mapTree:')
                // console.log(mapTree)
                // console.log('getBookmark->listTree:')
                // console.log(listTree)

                data.folder = data.mapTree
                let folderKey = Object.keys(data.mapTree)
                if (folderKey.length > 0) {
                    data.activeIndex = folderKey[0]
                    data.idChain.push(data.activeIndex)
                    data.bookMark = data.folder[data.activeIndex].children
                    console.log('getBookmark->activeIndex: ')
                    console.log(data.activeIndex)
                    console.log('getBookmark->bookmark:')
                    console.log(data.bookMark)
                    console.log('getBookmark->idChain:')
                    console.log(data.idChain)
                }
            }).catch((err) => {
                console.log('get bookmark from server fail')
                console.log(err)
                data.mapTree = JSON.parse(JSON.stringify(myData))
                data.folder = data.mapTree
                data.bookMark = data.folder['01'].children
            })
        }

        // 加载书签数据
        const loadBookmarkData = (bookmarkData) => {
            data.mapTree = Tools.deepCopy(bookmarkData)
            data.mapData = Tools.flatTree(bookmarkData)
            data.folder = data.mapTree

            let folderKey = Object.keys(data.mapTree)
            if (folderKey.length > 0) {
                data.activeIndex = folderKey[0]
                data.idChain.push(data.activeIndex)
                data.bookMark = data.folder[data.activeIndex].children
                console.log('loadBookmarkData->activeIndex: ')
                console.log(data.activeIndex)
                console.log('loadBookmarkData->bookmark:')
                console.log(data.bookMark)
                console.log('loadBookmarkData->idChain:')
                console.log(data.idChain)
            }
        }

        // 从服务器请求书签数据
        const getBookmarkServer = () => {
            BookmarkApi.findMineBookmark().then(res => {
                console.log('get bookmark from server')
                loadBookmarkData(res.body)
            }).catch(err => {
                getBookmarkCache()
            })
        }

        // 从缓存拿取书签数据，没有缓存则拿测试数据
        const getBookmarkCache = () => {
            let bookmarkData = CacheStore.getLocalStorage('bookmarkCache')
            if (bookmarkData !== '') {
                console.log('get bookmark from localStorage')
                loadBookmarkData(bookmarkData)
            } else {
                console.log('get bookmark from testData')
                loadBookmarkData(testData)
            }
        }

        // 设置用户，从cookie中读取，没有说明没登录
        const setUsername = (status = '') => {
            let access_token = CacheStore.getCookie('access_token')
            let userInfo = Tools.jsonParser(CacheStore.getCookie('userInfo'))
            if (access_token !== '' && userInfo !== '') {
                console.log('set userInfo from cookie')
                data.userInfo = userInfo
            } else {
                if (status === 'init') {
                    ElMessage.warning("未登录")
                }
                // token和userInfo有一个空了，就都清除，重新登录
                CacheStore.setCookie('access_token', '', -1)
                CacheStore.setCookie('userInfo', '', -1)
                console.log('no userInfo found in cookie')
                data.userInfo = {userid: 'unlogin', username: '未登录'}
            }
            getBookmarkServer()
        }
        setUsername(status = 'init')

        // 全部数据筛选功能
        watch(
            () => data.searchVal,
            () => {
                // 1、清空当前匹配书签
                data.searchMark = []
                // 2、获取搜索关键词组，去重
                let searchValue = data.searchVal
                // console.log('input: ', data.searchVal)
                let vals = [...new Set(Tools.getSearchValue(searchValue))]
                console.log(vals)

                // 3、每条书签匹配关键词
                Object.keys(data.mapData).forEach(key => {
                    let mark = data.mapData[key]
                    let title_match = [], desc_match = []
                    vals.forEach(val => {
                        mark.title.search(val) !== -1 && title_match.push(val)
                        mark.desc.search(val) !== -1 && desc_match.push(val)
                    })
                    // 如果有匹配到搜索关键词
                    if (title_match.length > 0 || desc_match.length > 0) {
                        let search_mark = JSON.parse(JSON.stringify(mark))
                        let title = search_mark.title, desc = search_mark.desc
                        title_match.forEach(match => {
                            title = title.replace(match, '<span style="color: #5aa2ff">' + match + '</span>')
                        })
                        desc_match.forEach(match => {
                            desc = desc.replace(match, '<span style="color: #5aa2ff">' + match + '</span>')
                        })
                        search_mark.title = title
                        search_mark.desc = desc
                        data.searchMark.push(search_mark)
                    }
                })
            }
        )
        // 书签类别选择，即左侧目录选择
        const selectType = (item, index) => {
            data.bookMark = item.children
            data.activeIndex = index
            // 切换类别，目录链重置
            data.idChain = []
            data.idChain.push(index)

            // 类别选择时，清空搜索书签
            data.searchVal = ''
            data.searchMark = []

            console.log('selectType->bookMark:')
            console.log(data.bookMark)
            console.log('selectType->idChain:')
            console.log(data.idChain)
        }
        // 书签跳转
        const navigate = (card) => {
            console.log('navigate->card:')
            console.log(card)
            if (card.type === 0) {
                // 进入子目录
                data.bookMark = card.children
                data.idChain.push(card.id)
                console.log('navigate->bookmark:')
                console.log(data.bookMark)
                console.log('navigate->idChain:')
                console.log(data.idChain)
                console.log('idChain.length:', data.idChain.length)
            } else {
                window.open(card.url)
            }
            // v.children
            //   ? (data.bookMark = v.value.children)
            //   : window.open(v.value.url, '_target')
        }
        // 返回上级目录
        const goBack = () => {
            let mark = data.mapTree
            data.idChain.pop()
            for (const id of data.idChain) {
                // 遍历目录id链，拿到上级目录内容
                mark = mark[id].children
            }
            data.bookMark = mark
            console.log('goback->bookMark:')
            console.log(data.bookMark)
            console.log(Object.keys(data.bookMark).length)
            console.log('goback->idChain:')
            console.log(data.idChain)
        }
        // 书签搜索跳转
        const searchNavigate = (card) => {
            let mark = data.mapTree
            let idChain = card.idChain.slice(0, -1)
            // 先拿到本card所在目录所有内容
            for (const id of idChain) {
                mark = mark[id].children
            }
            if (card.type === 0) {
                // card为目录则进入目录
                data.bookMark = mark[card.id].children
                data.idChain = card.idChain
            } else {
                data.bookMark = mark
                data.idChain = idChain
            }
            data.activeIndex = idChain[0]
            // 跳转后清空searchMark，与搜索内容searchVal
            data.searchMark = []
            data.searchVal = ''
        }

        function add() {
            let addId
            let keys = Object.keys(data.bookMark)
            let pid = data.idChain.at(-1)
            // 当前目录下有内容
            if (keys.length > 0) {
                // keys.sort()
                keys.sort((v1, v2) => {
                    return v1 - v2
                })
                // 最后一个key + 1得到新key
                let newId = (parseInt(keys.at(-1).replace(pid, '')) + 1).toString()
                addId = pid + (newId.length === 1 ? '0' + newId : newId)
            } else {
                addId = pid + '01'
            }

            let treeData = Tools.deepCopy(data.mapTree)
            convertDataTreeToSelectTree(treeData)
            data.toDialog = Object.assign(
                {
                    id: addId,
                    pid: pid,
                    type: 0,
                    flag: 'add',
                },
                {
                    treeData: Tools.mapTreeToListTree(treeData)
                },
                {
                    attachParams: {
                        level: 1
                    }
                }
            )
            console.log("add->toDialog:")
            console.log(data.toDialog)
            data.isDetailVisible = true
        }

        function addTop() {
            let addId
            let keys = Object.keys(data.folder)
            // 当前目录下有内容
            if (keys.length > 0) {
                // keys.sort()
                keys.sort((v1, v2) => {
                    return v1 - v2
                })
                // 最后一个key + 1得到新key
                let newId = (parseInt(keys.at(-1)) + 1).toString()
                addId = newId.length === 1 ? '0' + newId : newId
            } else {
                addId = '01'
            }

            data.toDialog = Object.assign(
                {
                    id: addId,
                    pid: '0',
                    type: 0,
                    flag: 'add',
                },
                {
                    treeData: []
                },
                {
                    attachParams: {
                        level: 0
                    }
                }
            )
            console.log("addTop->toDialog:")
            console.log(data.toDialog)
            data.isDetailVisible = true
        }

        // 编辑书签
        function edit(card) {
            let treeData = Tools.deepCopy(data.mapTree)
            convertDataTreeToSelectTree(treeData)
            data.toDialog = Object.assign(
                {...data.mapData[card.id]},
                {
                    pid: data.idChain.at(-1),
                    flag: 'edit'
                },
                {
                    treeData: Tools.mapTreeToListTree(treeData)
                }
            )
            data.isDetailVisible = true
        }

        // 删除书签
        function del(card) {
            let markTree = data.mapTree
            // 遍历目录id链，拿到当前目录下所有书签
            for (const id of data.idChain) {
                markTree = markTree[id].children
            }
            let mark = markTree[card.id]
            let idArray = []
            findIdFromMark(mark, idArray)

            ElMessageBox.confirm('确定删除此' + (card.type === '1' ? '书签' : '目录') + '? 其子项将被同步删除!', '警告', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                BookmarkApi.deleteByIds(idArray).then(() => {
                    refresh('delete', {id: card.id, idArray: idArray})
                }).catch(() => {
                    console.log('delete failed!')
                })
            })

        }

        /*
            迭代，目的是将mark及其下行节点的id和keyno填入idArray
            mark: 书签节点
            idArray: id数组
            将mark的id, keyno添加到idArray
        */
        function findIdFromMark(mark, idArray) {
            idArray.push(mark.id)

            let childs = mark.children
            for (const key of Object.keys(childs)) {
                let children = childs[key]
                findIdFromMark(children, idArray)
            }
        }

        // 去除书签树中的书签，只留目录
        function convertDataTreeToSelectTree(treeData) {
            let keys = Object.keys(treeData)
            for (const key of keys) {
                let card = treeData[key]
                if (card.type === 1) {
                    delete (treeData[key])
                } else {
                    card.isExtend = false
                    convertDataTreeToSelectTree(card.children)
                }
            }
        }

        // 用户登录
        const handleUserLogin = () => {
            data.isLoginVisible = true
        }
        const handleUserRegister = () => {
            data.isRegisterVisible = true
        }
        // 关闭弹窗事件
        const closeViews = (v) => (data.isDetailVisible = v)
        const closeLoginViews = (v) => (data.isLoginVisible = v)
        const closeRegisterViews = (v) => {data.isRegisterVisible = v}
        // 组件跳转
        const registerToLogin = () => {
            data.isRegisterVisible = false
            data.isLoginVisible = true
        }
        const loginToRegister = () => {
            data.isLoginVisible = false
            data.isRegisterVisible = true
        }

        // 获取书签分类数据
        const refresh = (type, bookmark) => {
            /*
                新增、编辑、删除书签后，需要进行刷新，否则需要重新向服务器请求全部书签数据
                刷新内容：
                bookmark: 当前目录下书签-----bookmark为从mapTree里的浅拷贝数据，因此后面可以考虑不用手动刷新它
                书签源数据：mapTree [listTree] mapData [listData]
             */
            let markTree = data.mapTree
            for (const id of data.idChain) {
                // 遍历目录id链，拿到上级目录内容
                markTree = markTree[id].children
            }
            console.log("refresh->data.bookMark:")
            console.log(data.bookMark)
            switch (type) {
                case 'delete':
                    console.log('refresh-delete:')
                    let idArray = bookmark.idArray
                    delete (markTree[bookmark.id])
                    for (const id in idArray) {
                        delete (data.mapData[id])
                    }
                    break
                case 'add':
                    /*
                        由于从Dialog组件传回的编辑结果，不包含children信息与idChain信息，
                        所以这里刷新时注意添加children和idChain
                     */
                    // data.bookMark[bookmark.id] = bookmark
                    console.log('refresh-add:')
                    markTree[bookmark.id] = Object.assign(bookmark, {children: {}})
                    let idChain = Tools.deepCopy(data.mapData[bookmark.pid].idChain)
                    data.mapData[bookmark.id] = Object.assign(bookmark, {idChain: idChain.push(bookmark.id)})
                    break
                case 'addTop':
                    /*
                        顶级目录添加后，没有上级idChain，idChain直接添加自己的id
                        直接使用data.mapTree和data.mapData，不要用遍历后的mapTree，否则会刷新错位置
                    */
                    console.log('refresh-addTop:')
                    data.mapTree[bookmark.id] = Object.assign(bookmark, {children: {}})
                    data.mapData[bookmark.id] = Object.assign(bookmark, {idChain: [bookmark.id]})
                    break
                case 'edit':
                    /*
                        由于从Dialog组件传回的编辑结果，不包含children信息与idChain信息，
                        所以这里刷新时不能直接赋值，否则会覆盖掉原本的children和idChain
                     */
                    // data.bookMark[bookmark.id] = Object.assign(data.bookMark[bookmark.id], bookmark)
                    console.log('refresh-edit:')
                    markTree[bookmark.id] = Object.assign(markTree[bookmark.id], bookmark)
                    data.mapData[bookmark.id] = Object.assign(data.mapData[bookmark.id], bookmark)
                    break
                case 'refresh':
                    // 如果pid有更改，需要改的内容太多，这里直接重新刷新
                    getBookmarkServer()
                    break
                default:
                    break
            }
        }
        // 删除书签
        const deleteClick = (row) => {
            const myData = JSON.parse(localStorage.getItem('BOOKMARK'))
            const delDetail = Object.assign(row, {
                type: data.mapTree[data.activeIndex].type
            })
            for (let i = 0; i < myData.length; i++) {
                if (delDetail.type === myData[i].type) {
                    const cindex = myData[i].children.findIndex(
                        (s) => s.title === delDetail.title
                    )
                    if (cindex > -1) {
                        myData[i].children.splice(cindex, 1)
                        localStorage.setItem('BOOKMARK', JSON.stringify(myData))
                        ElMessage.success('删除成功')
                        refresh()
                    }
                }
            }
        }
        // 退出登录
        const LoginOut = () => {
            ElMessageBox.confirm('确认要退出登录?', '温馨提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                const params = {username: '', password: ''}
                Api.logout(params.username, params.password).then((res) => {
                    Cookie.remove('userInfo')
                    setUsername()
                })
                ElMessage({
                    type: 'success',
                    message: '已退出登录'
                })
            })
        }
        // 登录或退出
        const loginClick = () => {
            handleUserLogin()
            // handleUserRegister()
        }

        // 导入书签
        const importBookmark = () => {
            if (data.userInfo.objectId) {
                const file = document.getElementById('file')
                file.dispatchEvent(new MouseEvent('click'))
                const mybookmark = document.getElementById('mybookmark')
                document.getElementById('file').addEventListener('change', function () {
                    var file = document.getElementById('file').files[0]
                    var reader = new FileReader()
                    reader.readAsText(file, 'utf-8')
                    reader.onload = function () {
                        mybookmark.innerHTML = reader.result
                        const formDatas = JSON.stringify(walkBookmarksTree(mybookmark))
                        if (formDatas) {
                            const params = {formDatas: formDatas}
                            saveBookmarkList(params)
                        }
                    }
                })
            } else {
                ElMessage.warning('请先登录')
            }
        }

        // 书签数据存储
        const saveBookmarkList = (params) => {
            saveObject('BOOKMARK', params).then((res) => {
                ElMessage.success('导入成功')
                getBookmarkList()
            })
        }

        return {
            deleteClick,
            ...toRefs(data),
            selectType,
            navigate,
            goBack,
            searchNavigate,
            add,
            addTop,
            edit,
            del,
            closeViews,
            closeLoginViews,
            closeRegisterViews,
            registerToLogin,
            loginToRegister,
            refresh,
            importBookmark,
            exportBookmark,
            loginClick,
            setUsername
        }
    },
    methods: {
        beforeEnter(el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter(el, done) {
            gsap.to(el, {
                opacity: 1,
                height: '1.6em',
                delay: el.dataset.index * 0.15,
                onComplete: done
            })
        },
        leave(el, done) {
            gsap.to(el, {
                opacity: 0,
                height: 0,
                delay: el.dataset.index * 0.15,
                onComplete: done
            })
        }
    }
}
</script>

<style scoped lang="scss">
#app {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
}

.bookmark {
    position: relative;
    margin-top: 10vh;
    width: 1200px;
    height: calc(80vh);
    border: 1px solid rgba(255, 255, 255, 0.18);
    box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
    border-radius: 6px;
    background: #fff;
    transition: 0.6s opacity;
    //opacity: 0;
    //
    //&:hover {
    //    opacity: 1;
    //}

    .left-box {
        width: 200px;
        height: 100%;
        border: 1px solid rgba(255, 255, 255, 0.18);
        box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
        position: relative;
        // overflow-y: auto;
        padding: 8px 0;

        img {
            width: 20px;
            height: auto;
            margin-right: 5px;
            cursor: pointer;
        }

        .active {
            box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
            background: #a0cae6;
        }

        .inactive {
            box-shadow: none;
            background: #fff;
        }

        .label {
            font-size: 14px;
            display: flex;
            cursor: pointer;
            border: none;
            position: relative;
            padding: 10px 15px;

            &:hover {
                box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
                background: #a0cae6;
            }

            .text-elipss {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
        }
    }

    .right-box {
        width: calc(100% - 200px);

        .card-s {
            width: 100%;
            //height: calc(100% - 30px);
            padding-top: 10px;
            max-height: calc(80vh - 98px);
            // height:calc(100% - 48px);
            display: flex;
            flex-wrap: wrap;
            overflow-x: hidden;
            overflow-y: auto;

            .card-item {
                cursor: pointer;
                width: calc(33% - 40px);
                display: flex;
                justify-content: flex-start;
                align-items: center;
                border: 1px solid rgba(255, 255, 255, 0.18);
                box-shadow: 0 8px 18px 0 rgba(31, 38, 135, 0.2);
                padding: 10px;
                margin: 7px 20px 7px 20px;
                position: relative;
                border-radius: 8px;
                max-height: 200px;
                height: 72px !important;

                &:hover {
                    transform: scale(1.06);
                    animation-delay: 0.3ms;
                    animation: 0.3ms;
                    box-shadow: 0 8px 18px 0 rgba(31, 38, 135, 0.3);
                }

                &:hover .logo-box-tools {
                    opacity: 0.85;
                }
            }
        }
    }
}

.tool-icon {
    width: 20px;
    height: 20px;
    object-fit: contain;
    display: inline-block;
    margin-right: 12px;
    cursor: pointer;

    &:hover {
        fill: '#3eaf7c';
    }
}

.tool-bar {
    height: 48px;
    border-bottom: 1px solid #eee;
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: rgb(250, 248, 248);

    .tool-logo {
        margin: 0 15px;

        a {
            color: #e03b5d;
            display: flex;
            align-items: center;
        }
    }

    .search-box {
        display: inline-block;
        position: relative;
        margin-right: 1rem;
        white-space: nowrap;

        img {
            position: absolute;
            top: 0;
            bottom: 0;
            z-index: 0;
            left: 0.6rem;
            margin: auto;
            width: 20px;
            height: auto;
        }

        input {
            text-align: initial;
            text-indent: initial;
            text-shadow: initial;
            text-transform: initial;
            word-spacing: initial;
            letter-spacing: initial;
            cursor: text;
            width: 14rem;
            height: 2rem;
            color: #4e6e8e;
            display: inline-block;
            border: 1px solid #eaecef;
            border-radius: 0.25rem;
            font-size: 0.9rem;
            line-height: 2rem;
            padding: 0 0.5rem 0 2rem;
            outline: none;
            transition: all 0.2s ease;
            background: transparent;
            background-size: auto;
            background-size: 1rem;
        }
    }
}

.box-m {
    display: flex;
    height: calc(100% - 50px);
}

.logo-img {
    width: 62px;
    height: 100%;
    margin-right: 10px;

    img {
        height: 100%;
        width: 100%;
        object-fit: contain;
        display: block;
        max-width: 70px;
        width: 62px;
    }
}

.logo-box {
    position: relative;
    flex: 1;

    .title {
        width: 100%;
        max-width: 145px;
        display: block;
        padding-top: 3px;
        font-size: 16px;
        font-weight: bold;
        color: #000000;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        display: block;
    }

    .subtitle {
        width: 100%;
        position: relative;
        max-width: 185px;
        margin-top: 5px;
        font-size: 13px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        color: rgba(0, 0, 0, 0.7);
        display: block;
    }
}

.list-complete-item {
    transition: all 0.8s ease;
    display: inline-block;
    margin-right: 10px;
}

.list-complete-enter-from,
.list-complete-leave-to {
    opacity: 0;
    transform: translateY(30px);
}

.list-complete-leave-active {
    position: absolute;
}

.card-item-nodata {
    height: calc(100% - 112px);
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;

    span {
        display: block;
        margin-top: 20px;
        color: #999;
    }
}

.bg {
    position: fixed;
    z-index: -999;
    position: fixed;
    height: 100%;
    width: 100%;
    background: url(./assets/bg.jpg);
}

.logo-box-tools {
    position: absolute;
    right: 0;
    top: 0;
    opacity: 0;
    transition: 0.4s opacity;

    i {
        padding: 4px;
        display: inline-block;

        &:hover {
            color: #e03b5d;
            background: #ff00001f;
            border-radius: 5px;
        }
    }
}

.import-tool {
    position: absolute;
    width: 100%;
    background: #fbf5f5;
    height: 36px;
    padding: 3px 15px;
    display: flex;
    align-items: center;
    bottom: 0;
    z-index: 99;

    i {
        font-size: 18px;
        margin: 1px 4px;
        padding: 4px;
        cursor: pointer;
        color: #e03b5d;
        background: #ff00001f;
        border-radius: 5px;
        opacity: 0.7;

        &:hover {
            color: #e03b5d;
            background: #c804041f;
            opacity: 1;
        }
    }
}

.left-box-item {
    height: 100%;
    overflow-y: auto;
    padding-bottom: 40px;
}

.import-text {
    font-size: 12px;
    color: #999;
    margin-right: 3px;
}

.el-icon-upload2 {
    position: relative;

    input {
        width: 1.46rem;
        height: 100%;
        z-index: 1;
        opacity: 0;
        position: absolute;
        cursor: pointer;
    }
}

.login-status {
    display: inline-block;
    font-size: 12px;
    padding-right: 8px;
    color: #999;
    cursor: pointer;
}

.login-s .tool-icon {
    margin-right: 5px;
}

.login-s:hover .login-status {
    color: #e03b5d;
}
</style>
